package com.example.springbootdemo.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.IOException;
import java.util.Set;

public class AtBeanPostProcessor implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    try {
      CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
      MetadataReader reader = factory.getMetadataReader(new ClassPathResource("com/example/springbootdemo/config/Config.class"));
      Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
      for (MethodMetadata method : methods) {
        String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        builder.setFactoryMethodOnBean(method.getMethodName(), "config");
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        if (!initMethod.isEmpty()) {
          builder.setInitMethodName(initMethod);
        }
        AbstractBeanDefinition bd = builder.getBeanDefinition();
        if (beanFactory instanceof DefaultListableBeanFactory context){
          context.registerBeanDefinition(method.getMethodName(), bd);
        }
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}
